#ifndef __EVENTLOOP_HPP__
#define __EVENTLOOP_HPP__

/* EventLoop模块：事件循环
    每个EventLoop都有一个任务队列，任务队列中的任务都在同一个单独的线程中执行。
    监听注册在当前EventLoop上的文件描述符的事件，当事件被触发时，调用相应的回调处理函数进行处理。如果不能立即处理，就将这个处理函数
    当作一个任务添加到任务队列中，等待执行。
 */

#include <sys/eventfd.h>

class EventLoop
{
    using Functor = std::function<void()>;// 任务类型定义
private:
    std::thread::id _tid;// EventLoop对应的线程id。对套接字的操作都在这个线程中执行以保证线程安全
    int _eventfd;// 事件通知模型。事件描述符。内部是一个计数器
    Poller _poller;// epoll模型，真正对事件监控进行操作
    TimerWheel _timerwheel;// 定时轮
    std::unique_ptr<Channel> _event_channel;// 对文件描述符上的事件监控进行管理，如添加、修改、删除事件监控，设置事件回调函数、开启/关闭监控
    std::vector<Functor> _tasks;// 任务队列，任务池
    std::mutex _mutex;// 互斥锁，保证对任务队列的操作是线程安全的
private:
    /**
     * @brief 创建事件通知模型。
     * 强制创建成功，创建失败则终止当前进程
     * 
     * @return int 创建好的事件通知模型，是一个文件描述符
     */
    static int CreateEventFd()
    {
        int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);// 非阻塞式 事件通知模型
        if(efd < -1)
        {
            ERR_LOG("eventfd failed");
            abort();
        }

        // DBG_LOG("eventfd: %d create success", efd);

        return efd;
    }

    /**
     * @brief 唤醒eventfd。
     * 向事件描述符中写入数据
     * 
     */
    void WakeUpEventFd()
    {
        uint64_t val = 1;// 这里必须是1
        ssize_t n = write(_eventfd, &val, sizeof(val));// 向eventfd中写入一个1
        if(n < 0)
        {
            if(errno == EINTR) return;// 信号中断

            ERR_LOG("wakeup eventfd failed");
            abort();
        }
    }
    /* 休眠eventfd。从eventfd读取数据，读取 事件通知的次数 */
    void ReadEventFd()
    {
        uint64_t val = 1;
        ssize_t n = read(_eventfd, &val, sizeof(val));// 读取到的n，表示事件通知的次数，因为次数无意义，所以不设置返回值，只关心读取成功与否
        if(n <= 0)
        {
            if(errno == EINTR || errno == EAGAIN) return;// 信号中断或没有数据可读

            ERR_LOG("read eventfd failed");
            abort();
        }
    }

    /**
     * @brief 判断任务是否属于当前Loop线程
     * 
     * @return true 是\
     * @return false 不是
     */
    bool IsInLoopThread() const
    {
        return std::this_thread::get_id() == _tid;
    }

    /**
     * @brief 将当前要执行的操作当作一个任务入任务队列
     * 对任务队列的操作要加入锁保护机制
     * 
     * @param task 任务
     */
    void QueuePushTask(const Functor& task)
    {
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _tasks.push_back(task);
        }
        // 有任务了，唤醒当前EventLoop对应的线程可能因为没有事件就绪而导致的epoll阻塞
        WakeUpEventFd();
    }

    /* 执行属于当前线程的任务 */
    void RunTasks()
    {
        // 1. 取出任务队列中的任务
        std::vector<Functor> tasks;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            tasks.swap(_tasks);
        }
        // 2. 执行任务
        for(auto& task : tasks)
        {
            task();
        }
    }
public:
    EventLoop(): _tid(std::this_thread::get_id()), _eventfd(CreateEventFd()), _timerwheel(this), _event_channel(new Channel(_eventfd, this))
    {
        _event_channel->SetReadCallback(std::bind(&EventLoop::ReadEventFd, this));
        _event_channel->EnableRead();
    }

    /**
     * @brief 更新文件描述符上的事件监控——未监控该描述符就添加对描述符的监控，否则就修改监控
     * 
     * @param channel 监控的文件描述符
     */
    void UpdateEvents(Channel* channel)
    {
        _poller.UpdateEvents(channel);
    }
    /**
     * @brief 移除对文件描述符的监控
     * 
     * @param channel 监控的文件描述符
     */
    void RemoveEvents(Channel* channel)
    {
        _poller.RemoveEvents(channel);
    }

    /**
     * @brief 执行任务——若任务不属于当前线程，就将任务添加到当前EventLoop的任务队列中
     * 
     * @param task 要执行的任务
     */
    void RunTaskInLoopThread(const Functor& task)
    {
        if(IsInLoopThread()) task();
        else QueuePushTask(task);
    }

    /**
     * @brief 启动事件循环
     * ——
     * 1. 开启事件监控，获取活跃连接（Channel） 
     * 2. 对活跃连接进行事件处理 
     * 3. 执行任务队列中的待执行的任务
     * 
     */
    void Start()
    {
        while(true)
        {
            // 1. 开启事件监控，获取活跃连接（Channel）
            std::vector<Channel*> actives;
            _poller.Poll(actives);
            // DBG_LOG("Poll finished");
            // 2. 对活跃连接进行事件处理
            for(auto channel : actives)
            {
                channel->HandleEvent();
            }
            // 3. 执行任务队列中的待执行的任务
            RunTasks();
        }
    }

    /* 添加定时任务 */
    void AddTimerTask(uint64_t task_id, const Functor& task, uint32_t timeout)
    {
        _timerwheel.AddTimerTask(task_id, task, timeout);
    }
    /* 刷新定时任务 */
    void RefreshTimerTask(uint64_t task_id)
    {
        _timerwheel.RefreshTimerTask(task_id);
    }
    /* 取消定时任务 */
    void CancelTimerTask(uint64_t task_id)
    {
        _timerwheel.CancelTimerTask(task_id);
    }
    /* 是否存在某个定时任务 */
    bool HasTimerTask(uint64_t task_id) const
    {
        return _timerwheel.HasTimerTask(task_id);
    }
};

void Channel::UpdateEvents()
{
    // DBG_LOG("Channel:UpdateEvents");
    _eventloop->UpdateEvents(this);
}
void Channel::RemoveEvents()
{
    // DBG_LOG("Channel:RemoveEvents");
    _eventloop->RemoveEvents(this);
}

void TimerWheel::AddTimerTask(uint64_t task_id, const TaskFunctor& task, uint32_t timeout)
{
    _eventloop->RunTaskInLoopThread(std::bind(&TimerWheel::AddTimerTaskInLoopThread, this, task_id, task, timeout));
    // ++_task_id;
}
void TimerWheel::RefreshTimerTask(uint64_t task_id)
{
    _eventloop->RunTaskInLoopThread(std::bind(&TimerWheel::RefreshTimerTaskInLoopThread, this, task_id));
}
void TimerWheel::CancelTimerTask(uint64_t task_id)
{
    _eventloop->RunTaskInLoopThread(std::bind(&TimerWheel::CancelTimerTaskInLoopThread, this, task_id));
}

#endif